msg_tool\scripts\emote/
rle.rs

1//! RL Encode used in mtn files
2use crate::ext::io::*;
3use anyhow::Result;
4use std::io::{Read, Seek, SeekFrom};
5
6const LZSS_LOOKAHED: usize = 1 << 7;
7
8/// Decompress RL data
9/// * `align` - alignment. usually 4
10/// * `actual_size` - if known, set it to preallocate memory
11pub fn rl_decompress<T: Read + Seek>(
12    mut input: T,
13    align: usize,
14    actual_size: Option<usize>,
15) -> Result<Vec<u8>> {
16    let mut output = if let Some(size) = actual_size {
17        Vec::with_capacity(size)
18    } else {
19        Vec::new()
20    };
21    let mut readed = input.stream_position()?;
22    let len = input.stream_length()?;
23    while readed < len {
24        let current = input.read_u8()? as usize;
25        readed += 1;
26        let count;
27        if (current & LZSS_LOOKAHED) != 0 {
28            count = (current ^ LZSS_LOOKAHED) + 3;
29            let buf = input.read_exact_vec(align)?;
30            readed += align as u64;
31            for _ in 0..count {
32                output.extend_from_slice(&buf);
33            }
34        } else {
35            count = (current + 1) * align;
36            let buf = input.read_exact_vec(count)?;
37            readed += count as u64;
38            output.extend_from_slice(&buf);
39        }
40    }
41    Ok(output)
42}
43
44fn compress_bound<T: Read + Seek>(input: &mut T, align: usize) -> Result<(usize, u8, Vec<u8>)> {
45    let pos = input.stream_position()?;
46    let mut curpos = pos;
47    let len = input.stream_length()?;
48    let mut buffer = vec![0u8; align];
49    let mut tmp = vec![0u8; align];
50    input.read_exact(&mut buffer)?;
51    curpos += align as u64;
52    let mut count = 1usize;
53    for _ in 1..LZSS_LOOKAHED + 2 {
54        if curpos >= len {
55            break;
56        }
57        input.read_exact(&mut tmp)?;
58        curpos += align as u64;
59        if buffer == tmp {
60            count += 1;
61        } else {
62            break;
63        }
64    }
65    input.seek(SeekFrom::Start(pos))?;
66    if count >= 3 {
67        return Ok((count, (count - 3) as u8 | LZSS_LOOKAHED as u8, buffer));
68    }
69    Ok((0, 0, buffer))
70}
71
72fn compress_bound_np<T: Read + Seek>(input: &mut T, align: usize) -> Result<(usize, u8)> {
73    let pos = input.stream_position()?;
74    let mut curpos = pos;
75    let len = input.stream_length()?;
76    input.seek_relative(align as i64)?;
77    curpos += align as u64;
78    let mut count = 1;
79    for _ in 1..LZSS_LOOKAHED {
80        if curpos >= len {
81            break;
82        }
83        let (ncount, _cmd, _buf) = compress_bound(input, align)?;
84        if ncount == 0 {
85            input.seek_relative(align as i64)?;
86            count += 1;
87            curpos += align as u64;
88        } else {
89            break;
90        }
91    }
92    input.seek(SeekFrom::Start(pos))?;
93    Ok((count, (count - 1) as u8))
94}
95
96/// Compress data using RL
97/// * `align` - alignment. usually 4
98pub fn rl_compress<T: Read + Seek>(mut input: T, align: usize) -> Result<Vec<u8>> {
99    let mut output = Vec::new();
100    let len = input.stream_length()?;
101    let mut readed = input.stream_position()?;
102    while readed < len {
103        let (count, cmd, buf) = compress_bound(&mut input, align)?;
104        if count > 0 {
105            output.push(cmd);
106            output.extend_from_slice(&buf);
107            readed += (count * align) as u64;
108            input.seek_relative((count * align) as i64)?;
109        } else {
110            let (ncount, ncmd) = compress_bound_np(&mut input, align)?;
111            output.push(ncmd);
112            let buf = input.read_exact_vec(ncount * align)?;
113            output.extend_from_slice(&buf);
114            readed += (ncount * align) as u64;
115        }
116    }
117    Ok(output)
118}